/**
 * @requires libs/json2
 * @requires libs/msgs
 */
define(['../libs/json2', '../libs/msgs', "../libs/select"], function() {

    /**
     * Form module
     *
     * @exports modules/form
     * @version {version}
     */

    /**
     * Form class
     * @class Frm
     * @return {Frm} An object instance
     */

    var Frm = base.Module.extend({
        options: {
            async: false,
            submitType: 'json',
            responseType: 'json',
            submitMethod: 'POST',
            msgLocation: '.content',
            noMsg: false,
            autoSubmit: false,
            autoUpdate: false,
            submitUrl: false,
            disabled: false,
            deferSubmit: false,
            target : "_self",
            successKey: 'server',
            type: "none",
            errorKey: 'server',
            commenttext: 'Terms of Submission &raquo;',
            commentlink: '/marlboro/comments/guidelines/'
        },

        /**
         * Constructor
         * @param {Object} opts
         * @param {DOM Element} element The DOM Element to attach to
         * @return {void}
         */
        init: function(opts, element) {
            this._super(opts, element);
            this.theForm = $('form', this.el);
            this.theFormValues = $('.form-values', this.el);
            this.theForm.attr('novalidate', 'novalidate');
            this.theForm.attr('method', this.options.submitMethod);
            this.action = this.theForm.attr('action');
            this.confirmDialog = $('#confirmDialog');
            this.frmModal = $('#alertModal');
            this._build();
        },
        /**
         *
         */
        _build: function() {
            //show form if no inline edit
            if (!this.theFormValues.length) {
                this.theForm.css({
                    'opacity': '1',
                    'display': 'block'
                });
            }

            $('.submit', this.el).on('click', $.proxy(function(e) {
                e.preventDefault();
                this.submitForm();
            }, this));

            //for handling styled radios in ie8
            if ($('html').hasClass('lt-ie9')) {
                $('.styled-radios', this.el).find('label').on('click', function() {
                    $(this).closest('.styled-radios').find('label').removeClass('checked');
                    $(this).addClass('checked');
                });
            }
            //ie7/8 single field enter submit fix
            if ($('html').hasClass('ie')){
                this.theForm.find('input, textarea, select').bind('keydown',$.proxy(function(e){
                    if(this.theForm.find('input[type="text"], input[type="number"], input[type="email"], input[type="tel"], input[type="date"]').length == 1){
                        if(e.keyCode == 13) {
                            e.preventDefault();
                            $('.save, .submit',this.el).trigger("click");
                        }
                    }
                    if(e.keyCode == 8 || e.keyCode == 37 || e.keyCode == 39){// backspace key, arrow left, arrow right
                        return true;
                    }

                },this));
            }

            //fix modal on orientation change
            $(window).on("orientationchange", function(){
                $('.frm .error-modal').css('display', 'none');
            });

            if(this.options.type == "update"){
                if($('.enable-buttons')[0]){
                    $('.enable').on('click', $.proxy(function(){
                        this.enableForm();
                    }, this));
                    $('.disable').on('click', $.proxy(function(){
                        this.disableForm();
                    }, this));
                    $(".disable").trigger("click");
                }
            }

            $(this.el).keydown($.proxy(function(event){
                if(event.keyCode == 13) {
                  event.preventDefault();
                  $(".submit", this.el).trigger("click");
                  return false;
                }
            }, this));

        },
        /**
         *
         */
        submitForm: function() {
            if (this.disabled) {
                return;
            }

            if (this.validateForm()) {
                if (this.options.async) {
                    this.contentType = this.options.submitType == "json" ? 'application/json' : 'application/x-www-form-urlencoded';
                    //add disabed to excluded fields to avoid being sent to the server
                    //http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
                    $(this.theForm).find('*[data-exclude]').attr('disabled', 'disabled');
                    //this utilizes crockfords json2 and custom function to massage data to better format
                    //http://jsfiddle.net/sxGtM/3/
                    this.formData = (this.options.submitType == "json") ? JSON.stringify(this.serializeForm()) : this.theForm.serialize();
                    if (!this.options.deferSubmit) {
                        this.sendData();
                    }
                } else if (this.options.submitUrl) {
                   window.open($('select', this.el).val(), this.options.target);
                } else {
                    if (this.action) {
                        this.theForm.submit();
                    }
                }

                this.publish('frm.submit', this.formData);
                this.log("submit");
            }
        },
        sendData: function() {
            $('.save, .submit', this.el).before($('<span class="loader"></span>'));
            // submit via ajax
            $.ajax({
                url: this.action,
                type: this.options.submitMethod,
                dataType: this.options.submitType,
                contentType: this.contentType,
                data: this.formData,
                cache: false,
                error: $.proxy(function(data) {
                    this.onSubmitError(data);
                }, this),
                success: $.proxy(function(data) {
                    this.onSubmitSuccess(data);
                }, this),
                complete: $.proxy(function(data) {
                    $('.loader', this.el).remove();
                    this.enableForm();
                    if (this.options.responseType == "html") {
                        this.onSubmitSuccess(data.responseText);
                    }
                }, this)
            });
            this.disableForm();
        },
        disableForm: function() {
            this.disabled = true;
            this.theForm.addClass('disabled').find('input, select, textarea').not('input[type="file"]').each(function(idx, el) {
                $(el).attr('disabled', 'disabled');
            });
            this.el.addClass('disabled');
            if($('.selectpicker')[0]){
                $('.selectpicker').prop('disabled',true);
                $('.selectpicker').selectpicker('refresh');
            }
            $btnGroup =  $('.btn-group').not(".enable-buttons, .toggle");
            $btnGroup.find(".active").addClass("no-action");
            $btnGroup.fadeTo('slow',.6).prop('disabled',true);
        },
        enableForm: function() {
            this.disabled = false;
            this.theForm.removeClass('disabled').find('input:not(.locked), select, textarea').each(function(idx, el) {
                $(el).removeAttr('disabled');
            });
            this.el.removeClass('disabled');
            if($('.selectpicker')[0]){
                $('.selectpicker').prop('disabled',false);
                $('.selectpicker').selectpicker('refresh');
            }
            $btnGroup =  $('.btn-group').not(".enable-buttons, .toggle");
            $btnGroup.find(".active").removeClass("no-action");
            $btnGroup.fadeTo('slow',1).prop('disabled',false);
        },
        /**
         *
         */
        onSubmitSuccess: function(data) {
            var success = 'success',
                error = 'error',
                msg = '';
            var $content = $(this.frmModal).find('.modal-content')[0];

            if (this.options.responseType == "json" || !this.action) {
                if (data.responseCode === "000") {
                    if (this.theFormValues.length) {
                        this.updateFormValues();
                        base.toggleElems(this.theFormValues, this.theForm);
                        this.showMessage(success, base.msgs['formsuccess'][this.options.successKey], this.theFormValues);
                    } else {
                        this.showMessage(success, base.msgs['formsuccess'][this.options.successKey], $(this.options.msgLocation, this.theForm));
                    }
                    this.successSubmit();
                    this.publish('frm.success', data);
                } else {
                    base.showError( data.msgError);
                    this.publish('frm.error', data);
                }
            } else if (this.options.responseType == "html") {
                if((data.indexOf('<script') != -1) && (data.indexOf('application/json') == -1)) {
                    var scriptContent = data.substring(data.indexOf('<script'), data.length);
                    scriptContent = scriptContent.substring(scriptContent.indexOf('>') + 1, scriptContent.indexOf('</'));
                    $(this.options.msgLocation).html(data);
                    $(this.options.msgLocation).append('<sc'+'ript type="text/javascript">' + scriptContent +'</sc'+'ript>');
                }
                else {
                    $(this.options.msgLocation).html(data);
                }
                this.publish('frm.success', data);
            }
            this.el.removeClass('edit');
        },
        successSubmit: function(){
            switch(this.options.type){
                case 'add':
                    this.resetForm();
                    base.showSuccess("Record was added successfully.");
                break;
                case 'update':
                    base.showSuccess("Record was updated successfully.");
                    setTimeout($.proxy(function(){
                        $(".disable").trigger("click");
                    }, this), 1000);
                break;

            }
        },
        /**
         *
         */
        onSubmitError: function(data) {
            this.showMessage('error', base.msgs['formerror'][this.options.errorKey], $(this.options.msgLocation, this.theForm));
            this.publish('frm.error', data);
        },
        /**
         *
         */
        showMessage: function(type, msg, location) {
            this.msgWrapper = $('<div></div>').addClass(type);
            this.msgDiv = $('<div></div>');
            this.icnSpan = $('<span></span>');
            $(this.msgWrapper).append(this.msgDiv);
            if (type == "error") {
                this.msgWrapper.addClass('error1');
                $(this.icnSpan).addClass('icn-warning left');
            } else {
                $(this.icnSpan).addClass('icn-tick left');
            }
            $(this.msgDiv).empty().append(this.icnSpan).append(msg);
            location.prepend(this.msgWrapper);
        },
        /**
         *
         */
        resetForm: function() {
            $(this.msgWrapper).empty().remove();
            $('.icn-tick, .icn-warning', this.el).not('.doi .icn-warning').remove();
            $('.invalid', this.el).removeClass('invalid');
            this.el.find('input[type], textarea, select').unbind('blur');
            this.el.find('input[type="checkbox"]').unbind('click');

            this.theForm[0].reset();

            this.publish('frm.reset');
        },
        /**
         *
         */
        updateFormValues: function() {
            this.theFormValues.find('*[data-name]').each($.proxy(function(idx, el) {
                var $el = $(el);
                var field = this.theForm.find('*[name="' + $el.data('name') + '"]');

                if (field[0] && field[0].nodeName.toLowerCase() === 'select') {
                    $option = field.find('option:selected');
                    $el.html($option.val());
                } else {
                    $el.html(field.val());
                    field.attr('value', field.val());
                    //ie fix
                    if (field[0]) field[0].defaultValue = field.val();
                }
            }, this));

            this.el.find('select').each(function(idx,el){
                $option = $(el).find('option:selected');
                $(el).find('option').each(function(idx,el){
                    $(el).removeAttr('selected');
                    $(el)[0].defaultSelected = false;
                });
                $option.attr('selected','selected');
                //ie fix
                $option[0].defaultSelected = true;
            });

            this.theForm.find('input[type="checkbox"]:not(:checked)').removeAttr('checked');
            this.theForm.find('input[type="checkbox"]:checked').attr('checked','checked');
        },
        /**
         *
         */
        serializeForm: function() {
            var formJson = {};
            var formArr = this.theForm.serializeArray();

            for (var item in formArr) {
                if (formArr.hasOwnProperty(item)){
                    var formItem = formArr[item];
                    //hack for grouped items
                    formName = (formItem.name.split('['));
                    if (formName.length > 1) {
                        if (formJson[formName[0]] === undefined) {
                            formJson[formName[0]] = {};
                        }
                        formJson[formName[0]][formName[1].split(']')[0]] = formItem.value;
                    } else {
                        formJson[formItem.name] = formItem.value || "";
                    }
                }
            }
            return formJson;
        },
        /**
         *
         */
        validateForm: function() {
            //validation will live here. Temporary Number Validation
            var regN = /^-{0,1}\d*\.{0,1}\d+$/;
            var toapply = [];
            var clean = true;
            var errcnt = 0;
            var fldcnt = 0;
            var fldinvalid = false;
            var errorMessage = [];

            this.el.find('input[type], textarea, select').each($.proxy(function(idx, el) {

                var $el = $(el);

                if (this.el.find('input[type], textarea, select').length > 1) {
                    $el.off('blur').on('blur', $.proxy(function() {
                        this.validateForm();
                    }, this));
                }

                if (this.el.find('input[type="checkbox"]:not(.exclude)').length > 0) {
                    $el.off('click').on('click', $.proxy(function() {
                        this.validateForm();
                    }, this));
                }

                var rqd = $el.is('[required]') || $el.attr('required');

                fldinvalid = false;

                if ($el.attr('type') != 'hidden' && $el.attr('type') != 'radio') fldcnt++;

                if ((!$el.val() || $el.val() === '' || $el.val() == $el.attr('placeholder')) && !rqd) {
                    $el.removeClass('invalid');
                    $el.nextAll('span, .error-modal').remove();
                } else {
                    //TODO: Change this to conform to HTML5 Form API Spec
                    var ml = $el.attr('maxlength');
                    if (ml && ml !== '' && ml > 0) {
                        toapply.push({
                            v: 'maxlength',
                            t: parseInt(ml, 10)
                        });
                    }

                    var rule = $el.data('rules');

                    if (rule) {
                        var rules = rule.split(',');
                        var i = rules.length;
                        while (i--) {
                            var ra = rules[i].split(':');
                            var rid = ra[0].split(' ').join();
                            if (this.validators[rid]) {
                                toapply.push({
                                    v: rid,
                                    t: ra[1]
                                });
                            }
                        }
                    }

                    var vdr;
                    if ($el.get(0).nodeName.toLowerCase() == "input") {
                        vdr = $el.attr('type');
                    } else {
                        vdr = $el.get(0).nodeName.toLowerCase();
                    }
                    if (vdr && this.validators[vdr]) toapply.push({
                        v: vdr
                    });

                    var n = toapply.length;

                    while (n--) {
                        var o = toapply.pop();
                        var result = (o.t) ? this.validators[o.v].apply(this, [$el, o.t]) : this.validators[o.v].apply(this, [$el]);

                        $elem = $el.parent().is('.styled-select, .styled-text') ? $el.parent() : $el;

                        if (result !== true) {
                            $elem.nextAll('span, .error-modal').remove();
                            errorMessage.push([$el, result]);
                            $elem.addClass('invalid');

                            if (!this.options.noMsg && !$elem.hasClass('styled-text')) {
                                $elem.after($('<span class="icn-warning js-msg-div"></span>'));
                            }

                            var errorModal = $('<div class="error-modal">' + result + '</div>');
                            if (!$elem.nextAll('.error-modal').length) {
                                $elem.off('mouseover').on('mouseover', function(e) {
                                    $(this).nextAll('.error-modal').css({
                                        'left': $(this).position().left,
                                        'top': $(this).position().top + $(this).height() + 4
                                    });
                                    base.toggleElem($(this).nextAll('.error-modal'));
                                });

                                $elem.off('mouseout').on('mouseout', function() {
                                    if ($(this).nextAll('.error-modal').hasClass('active')) base.toggleElem($(this).nextAll('.error-modal'));
                                });
                                $elem.after($(errorModal));
                            }

                            clean = false;
                            fldinvalid = true;
                            errcnt++;

                        } else {
                            if (!fldinvalid) {
                                $elem.nextAll('span, .error-modal').remove();
                                $elem.removeClass('invalid');
                                if (!($elem.get(0).nodeName.toLowerCase() == 'input' && ($elem.attr('type') == 'radio' || $elem.attr('type') == 'checkbox'))) {
                                    if (!this.options.noMsg && !$elem.hasClass('styled-text')) {
                                        $elem.after($('<span class="icn-tick js-ok-msg"></span>'));
                                    }
                                }
                            }
                        }
                    }
                }
            }, this));

            $(this.msgWrapper).remove();

            if (!clean) {
                this.publish('frm.invalid');
                this.showMessage('error', base.msgs.formerror.submission, $(this.options.msgLocation, this.theForm));
            } else {
                this.publish('frm.valid');
                return true;
            }
            this.log("Validation Complete", this.el);
        },
        /**
         *
         */
        validators: {
            email: function(el) {
                var regE = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
                if (regE.test(el.val()) || (!el.attr('required') && !el.is('[required]')) && (!el.val() || el.val() == el.attr('placeholder'))) {
                    return true;
                }
                return ('' + base.msgs['formerror']['email']).split('%title%').join(el.attr('title'));
            },
            tel: function(el) {
                var regE = /^\(?(\d{3})\)?[\- ]?(\d{3})[\- ]?(\d{4})$/;
                if (regE.test(el.val()) || (!el.attr('required') && !el.is('[required]')) && (!el.val() || el.val() == el.attr('placeholder'))) {
                    return true;
                }
                return ('' + base.msgs['formerror']['tel']).split('%title%').join(el.attr('title'));
            },
            text: function(el) {
                if (el.val() && el.val() != el.attr('placeholder')) {
                    return true;
                }
                return ('' + base.msgs['formerror']['required']).split('%title%').join(el.attr('title'));
            },
            state: function(el) {
                if (el.val() && el.val() != el.attr('placeholder')) {
                    return true;
                }
                return ('' + base.msgs['formerror']['state']).split('%title%').join(el.attr('title'));
            },
            password: function(el) {
                if (el.val() && el.val() != el.attr('placeholder')) {
                    return true;
                }
                return ('' + base.msgs['formerror']['required']).split('%title%').join(el.attr('title'));
            },
            match: function(el, target) {
                if (el.val() && el.val() != el.attr('placeholder') && el.val() == $('[name="' + target + '"]').val()) {
                    return true;
                }
                return ('' + base.msgs['formerror']['match']).split('%title%').join(el.attr('title')).split('%target_title%').join($('[name="' + target + '"]').attr('title'));
            },
            matchId: function(el, target) {
                if (el.val() && el.val() != el.attr('placeholder') && el.val() == $('[id="' + target + '"]').val()) {
                    return true;
                }
                return ('' + base.msgs['formerror']['matchId']).split('%title%').join(el.attr('title')).split('%target_title%').join($('[id="' + target + '"]').attr('title'));
            },
            minlength: function(el, target) {
                if (!el.val() || el.val() == el.attr('placeholder') || el.val().length >= target) {
                    return true;
                }
                var msg = el.data('error-msg') ? el.data('error-msg') : base.msgs['formerror']['minlength'];
                return ('' + msg).split('%title%').join(el.attr('title')).split('%target%').join(target).split('%used%').join(el.val().length);
            },
            maxlength: function(el, target) {
                if (!el.val() || el.val() == el.attr('placeholder') || el.val().length <= target) {
                    return true;
                }
                return ('' + base.msgs['formerror']['maxlength']).split('%title%').join(el.attr('title')).split('%target%').join(target).split('%used%').join(el.val().length);
            },
            textarea: function(el) {
                if (el.val() && el.val() != el.attr('placeholder')) {
                    return true;
                }
                return ('' + base.msgs['formerror']['required']).split('%title%').join(el.attr('title'));
            },
            checkbox: function(el) {
                if (el.attr("checked") || (!el.attr('required') && !el.is('[required]'))) {
                    return true;
                }
                return ('' + base.msgs['formerror']['checkbox']).split('%title%').join(el.attr('title'));
            },
            radio: function(el) {
                if ($('input[name="' + el.attr('name') + '"]:checked').val() || !el.attr("required")) {
                    return true;
                }
                return ('' + base.msgs['formerror']['checkbox']).split('%title%').join(el.attr('title'));
            },
            select: function(el) {
                if (el.val() || (!el.attr('required') && !el.is('[required]'))) {
                    return true;
                }
                return ('' + base.msgs['formerror']['required']).split('%title%').join(el.attr('title'));
            },
            number: function(el) {
                var regE = /^\s*\d+\s*$/;
                if (regE.test(el.val())) {
                    return true;
                }
                return ('' + base.msgs['formerror']['number']).split('%title%').join(el.attr('title'));
            },
            date: function(el) {
                if (el.val() || (!el.attr('required') && !el.is('[required]'))) {
                    return true;
                }
                return ('' + base.msgs['formerror']['required']).split('%title%').join(el.attr('title'));
            }
        }
    });

    base.plugin('frm', Frm, '.frm');

    return Frm;
});